#include "MapResizer.hpp"

bool MapResizer::needsResize(Map* map, int x, int y){
    return x<0||x>=map->width||y<0||y>=map->height;
}

bool MapResizer::needsResize(Map* map, sf::Rect<int>* marquee){
    return marquee->width > 0 && marquee->height > 0 && 
            (marquee->left < 0 || marquee->top < 0 || 
            marquee->left + marquee->width > map->width || 
            marquee->top + marquee->height > map->height);
}

bool MapResizer::canResize(Map* map, int x, int y){
    if(!needsResize(map, x, y)) return true;

    int newX = 0;
    int newY = 0;
    
    if(x >= map->width) newX = x - map->width + 1;
    else if(x < 0){
        newX = x*-1;
    }
    
    if(y >= map->height) newY = y - map->height + 1;
    else if(y < 0){
        newY = y*-1;
    }
    return ((map->width+newX)*(map->height+newY) < 65535);
}



MapResizer::ResizeInfo* MapResizer::resizeByMarquee(Map* map, sf::Rect<int>* marquee){
    
    ResizeInfo* resizeInfo = new ResizeInfo;
    
    if(marquee->left >= 0){
        resizeInfo->shiftX = 0;
        if(marquee->left + marquee->width > map->width){
            resizeInfo->resizeX = (marquee->left + marquee->width) - map->width;
        }else{
            resizeInfo->resizeX = 0;
        }
    }else if(marquee->left < 0 && marquee->width + marquee->left >= map->width){
        resizeInfo->shiftX = 0 - marquee->left;
        resizeInfo->resizeX = resizeInfo->shiftX + ((marquee->left + marquee->width) - map->width);
    }else if(marquee->left < 0){
        resizeInfo->shiftX = 0 - marquee->left;
        resizeInfo->resizeX = resizeInfo->shiftX;
    }
    
    if(marquee->top >= 0){
        resizeInfo->shiftY = 0;
        if(marquee->top + marquee->height > map->height){
            resizeInfo->resizeY = (marquee->top + marquee->height) - map->height;
        }else{
            resizeInfo->resizeY = 0;
        }
    }else if(marquee->top < 0 && marquee->height + marquee->top >= map->height){
        resizeInfo->shiftY = 0 - marquee->top;
        resizeInfo->resizeY = resizeInfo->shiftY + ((marquee->top + marquee->height) - map->height);
    }else if(marquee->top < 0){
        resizeInfo->shiftY = 0 - marquee->top;
        resizeInfo->resizeY = resizeInfo->shiftY;
    }
    
    if(MapResizer::resizeByInfo(map, resizeInfo) == 0){
        delete resizeInfo;
        return 0;
    }
    return resizeInfo;
}

MapResizer::ResizeInfo* MapResizer::resizeByInfo(Map* map, MapResizer::ResizeInfo* resizeInfo){
    int destWidth = map->width + resizeInfo->resizeX;
    int destHeight = map->height + resizeInfo->resizeY;
    int destMapSize = destWidth*destHeight;
    if(destMapSize >= NO_SPRITE)
        return 0;
    
    int copyWidth = map->width;
    int copyHeight = map->height;
    int copyMapSize = copyWidth*copyHeight;

    
    int destLeftStart = 0;
    if(resizeInfo->shiftX > 0){
        destLeftStart = resizeInfo->shiftX;
    }
    int destTopStart = 0;
    if(resizeInfo->shiftY > 0){
        destTopStart = resizeInfo->shiftY;
    }
    
    int copyLeftStart = 0;
    if(resizeInfo->shiftX < 0)
        copyLeftStart = 0 - resizeInfo->shiftX;
    int copyTopStart = 0;
    if(resizeInfo->shiftY < 0)
        copyTopStart = 0 - resizeInfo->shiftY;
    
    Tile* newTiles = new Tile[destMapSize];
    
    //Loop all layers
    for (int l = 0; l < LAYERS; l++) {

        //If the map is shifted down then pad the top with empty space.
        for(int r = 0; r < destTopStart; r++){
            for(int c = 0; c < destWidth; c++){
                newTiles[r*destWidth + c][l] = NO_SPRITE;
            }
        }
        
        //Copy rows from old map to new map.
        for(int r = destTopStart; r < destHeight && r - destTopStart < copyHeight; r++){
            //If the map is shifted right then pad the left with empty space.
            for(int c = 0; c < destLeftStart; c++){
                newTiles[r*destWidth + c][l] = NO_SPRITE;
            }
            
            //Copy tiles
            for(int c = destLeftStart; c < destWidth && c - destLeftStart < copyWidth; c++){
                newTiles[r*destWidth + c][l] = map->tiles[(r-destTopStart + copyTopStart)*copyWidth + (c - destLeftStart) + copyLeftStart][l];
            }
            
            //If destination ends after copy width then pad the right side with empty space.
            for(int c = copyWidth; c < destWidth - destLeftStart; c++){
                newTiles[r*destWidth + (c + destLeftStart)][l] = NO_SPRITE;
            }
        }
        
        //If destination ends after copy height then pad the bottom with empty space.
        for(int r = copyHeight; r < destHeight - destTopStart; r++){
            for(int c = 0; c < destWidth; c++){
                newTiles[(r + destTopStart)*destWidth + c][l] = NO_SPRITE;
            }
        }
    }
    
    for(int r = destTopStart; r < destHeight && r - destTopStart < copyHeight; r++){
           //Copy tile properties
            for(int c = destLeftStart; c < destWidth && c - destLeftStart < copyWidth; c++){
                newTiles[r*destWidth + c].properties = map->tiles[(r-destTopStart + copyTopStart)*copyWidth + (c - destLeftStart) + copyLeftStart].properties;
            }
    }
    
    delete map->tiles;
    map->tiles = newTiles;
    map->width = destWidth;
    map->height = destHeight;
    return resizeInfo;
}

void MapResizer::shrink(Map* map, MapResizer::ResizeInfo* info){
    MapResizer::ResizeInfo shrinkInfo;
    
    shrinkInfo.resizeX = 0 - info->resizeX;
    shrinkInfo.resizeY = 0 - info->resizeY;
    if(info->shiftX == 0)
        shrinkInfo.shiftX = 0;
    else
        shrinkInfo.shiftX = 0 - info->shiftX;
    
    if(info->shiftY == 0)
        shrinkInfo.shiftY = 0;
    else
        shrinkInfo.shiftY = 0 - info->shiftY;
    
    MapResizer::resizeByInfo(map, &shrinkInfo);
}